#include "people_id_file.h"
#include <iostream>
#include <fstream>
#include <filesystem>  // c++17

#include <nlohmann/json.hpp>


#define sys_log(fmt, ...) \
    printf("[INFO]: " fmt "\n", ##__VA_ARGS__)

/* 这个在linux终端中打印出的error是红色，更加醒目 */
#define err_log(fmt, ...) \
    printf("\033[31m[ERROR]: " fmt "\033[0m\n", ##__VA_ARGS__)




namespace fs = std::filesystem;
using json = nlohmann::json;

// 构造函数
PatientManager::PatientManager(const std::string& directory)
    : directory_(directory) {
    initialize();
}

// 初始化函数：读取所有患者信息
void PatientManager::initialize() {
    patients_.clear(); // 清空现有数据

    // 遍历目录
    for (const auto& entry : fs::directory_iterator(directory_)) {
        if (entry.is_directory()) {
            // 获取文件夹名称（患者 ID）
            std::string folderName = entry.path().filename().string();
            try {
                int id = std::stoi(folderName); // 尝试将文件夹名称转换为整数 ID

                // 读取患者信息
                PeopleInfo info;
                if (readPatientInfo(entry.path().string(), id, info)) {
                    patients_.push_back(info);
                }
            } catch (const std::invalid_argument& e) {
                // std::cerr << "Invalid folder name (not a number): " << folderName << std::endl;
                err_log("Invalid folder name (not a number): %s", folderName.c_str());
            }

        }// end of 'if'

    }// end of 'for'
}

// 读取单个患者信息
bool PatientManager::readPatientInfo(
    const std::string& folderPath, int id, PeopleInfo& info) const {

    info.id = id;

    // 构建 info.json 文件路径
    std::string jsonFilePath = folderPath + "/info.json";

    // 检查文件是否存在
    if (!fs::exists(jsonFilePath)) {
        // std::cerr << "File not found: " << jsonFilePath << std::endl;

        err_log("File not found: %s", jsonFilePath.c_str());

        return false;
    }

    // 尝试打开并解析 JSON 文件
    std::ifstream jsonFile(jsonFilePath);
    if (!jsonFile.is_open()) {
        // std::cerr << "Failed to open file: " << jsonFilePath << std::endl;

        err_log("Failed to open file: %s", jsonFilePath.c_str());

        return false;
    }

    // 尝试读取json字段中的name和region
    try {
        json jsonData;
        jsonFile >> jsonData;

        // 读取 name 和 region
        info.name = jsonData.at("name").get<std::string>();
        info.region = jsonData.at("region").get<std::string>();
    } catch (const json::exception& e) {
        // std::cerr << "JSON parsing error in file " << jsonFilePath << ": " << e.what() << std::endl;
        err_log("JSON parsing error in file: %s", e.what());
        return false;
    }

    return true;
}

std::optional<std::reference_wrapper<const PeopleInfo>> PatientManager::findById(int id) const {
    for (const auto& patient : patients_) {
        if (patient.id == id) {
            return patient; // 返回引用
        }
    }
    return std::nullopt; 
}

std::optional<std::reference_wrapper<const PeopleInfo>> PatientManager::findByName(const std::string& name) const {
    for (const auto& patient : patients_) {
        if (patient.name == name) {
            return patient; // 返回引用
        }
    }
    return std::nullopt; 
}


// 更新患者信息
void PatientManager::update() {
    initialize();
}

// 添加患者信息到缓冲区
void PatientManager::addPatient(const PeopleInfo& info) {
    patients_.push_back(info);
}

// 写入单个患者信息到文件夹
void PatientManager::writePatientInfo(const PeopleInfo& info) const {
    // 创建患者文件夹
    std::string folderPath = directory_ + "/" + std::to_string(info.id);
    fs::create_directory(folderPath);

    // 构建 info.json 文件路径
    std::string jsonFilePath = folderPath + "/info.json";

    // 创建 JSON 对象
    json jsonData;
    jsonData["name"] = info.name;
    jsonData["region"] = info.region;

    // 写入文件
    std::ofstream jsonFile(jsonFilePath);
    if (jsonFile.is_open()) {
        jsonFile << jsonData.dump(4); // 4 是缩进空格数
    } else {
        // std::cerr << "Failed to write file: " << jsonFilePath << std::endl;
        err_log("Failed to write file: %s", jsonFilePath.c_str());
    }
}

// 将缓冲区的所有患者信息写入文件夹
void PatientManager::saveAllPatients() {
    for (const auto& patient : patients_) {
        writePatientInfo(patient);
    }
}
